#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#include <sys/syscall.h>
typedef void*(* func)(void*);

struct Func{
    func runner;
    void* arg;
};

struct ThreadPool{
    struct Func* threads_queue;
    int max_threads;
    int item_num;
    int consume_num;
    int dequeue;
    int enqueue;
    sem_t emptyslot;
    sem_t sem_mutex;
    sem_t stock;
    int END_FLAG;
    
    
};









int ThreadPool_threads_push_with_sem(struct ThreadPool* pool, func runner, void* arg){
    if (pool->item_num < pool->max_threads) {
        sem_wait(&pool->emptyslot);
        sem_wait(&pool->sem_mutex);

        if (pool->item_num < pool->max_threads) {
            pool->item_num++;


            (pool->threads_queue[pool->enqueue]).runner = runner;
            (pool->threads_queue[pool->enqueue]).arg = arg;

            pool->enqueue = (pool->enqueue + 1) % pool->max_threads;
            


            if (pool->item_num == pool->max_threads)
                pool->END_FLAG = 1;
            sem_post(&pool->stock);
        } 
        else {
            sem_post(&pool->emptyslot);
        }
        sem_post(&pool->sem_mutex);
        sleep(1);
    }
}

int ThreadPool_threads_pop_with_sem(struct ThreadPool* pool){
     if ((pool->consume_num < pool->item_num) || (pool->END_FLAG == 0))  { 
        sem_wait(&pool->stock);  /* if stock is empty and all producers stop working at this point, one or more consumers may wait forever */
        sem_wait(&pool->sem_mutex);
        if (pool->consume_num < pool->item_num) { 
            pool->dequeue = (pool->dequeue + 1) % pool->max_threads;
            pool->consume_num++;
            sem_post(&pool->emptyslot);
        }
        else {
            sem_post(&pool->stock);
        }
        sem_post(&pool->sem_mutex);
    }
    
}

int ThreadPool_init(struct ThreadPool* pool,int maxThreads){

    pool->threads_queue = (struct Func*)malloc( (maxThreads + 1) * sizeof(struct Func));


    pool->max_threads = maxThreads;



    pool->item_num = 0;
    pool->consume_num = 0;
    pool->enqueue = 0;
    pool->dequeue = 0;
    pool->END_FLAG = 0;

    int ret = sem_init(&pool->sem_mutex, 1, 1); /* the second parameter of sem_init must be set to non-zero for inter process sharing */
    if (ret == -1) {
        perror("sem_init-mutex");
       
    }
    ret = sem_init(&pool->stock, 1, 0); /* initialize to 0 */
    if (ret == -1) {
        perror("sem_init-stock");
        
    }
    ret = sem_init(&pool->emptyslot, 1, pool->max_threads); /*initialize to BUFFER_SIZE */
    if (ret == -1) {
        perror("sem_init-emptyslot");
        
    }

}

void ThreadPool_destroy(struct ThreadPool* pool){
    sem_destroy(&pool->stock);
    sem_destroy(&pool->emptyslot);
    sem_destroy(&pool->sem_mutex);
    free(pool->threads_queue);
}

static void *runner(void *param)
{
    int randnum = rand() % 20;
    printf("id:%ld  randnum:%d\n", pthread_self(), randnum);
    pthread_exit(0);

}

static void* hello(void* arg){
    struct ThreadPool* pool = (struct ThreadPool*)arg;
    ThreadPool_threads_push_with_sem(pool, runner, NULL);
}
int ThreadPool_print(struct ThreadPool* pool,int num){

    for (int i = 0; i < num ; ++ i){
        printf("runner %p func runner %p\n",runner, (pool->threads_queue)[i].runner);
    }

}
int main(){

    struct ThreadPool pool;
    ThreadPool_init(&pool, 11);
    

    pthread_t T[5];
    for(int i = 0; i < 5 ; ++ i){
        pthread_create(&T[i],NULL,hello,&pool);
    }

    for(int i = 0; i < 5; ++ i){
        pthread_join(T[i], NULL);
    }

    ThreadPool_print(&pool, 5);
    ThreadPool_destroy(&pool);

}